<head>
  <base>
  <script src="https://fastly.jsdelivr.net/npm/marked@3.0.7/marked.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.js" crossorigin="anonymous"></script>
  <script>
    const renderer = new marked.Renderer();
    var options = {
      renderer: renderer,
      pedantic: false,
      gfm: true,
      breaks: false,
      sanitize: false,
      smartLists: true,
      smartypants: false,
      xhtml: false
    };
    marked.setOptions(options);

    function htmldecode(text){
      var temp = document.createElement("div");
      temp.innerHTML = text;
      var output = temp.innerText || temp.textContent;
      temp = null;
      return output;
    }

    function mathsExpression(expr) {
      if (expr.match(/^\$\$[\s\S]*\$\$$/)) {
        expr = expr.substr(2, expr.length - 4);
        return katex.renderToString(expr, { displayMode: true });
      } else if (expr.match(/^\$[\s\S]*\$$/)) {
        expr = htmldecode(expr); // temp solution
        expr = expr.substr(1, expr.length - 2);
        return katex.renderToString(expr, { displayMode: false });
      }
    }

    const unchanged = new marked.Renderer()
    renderer.code = function(code, lang, escaped) {
      if (!lang) {
        const math = mathsExpression(code);
        if (math) {
          return math;
        }
      }
      return unchanged.code(code, lang, escaped);
    };

    renderer.codespan = function(text) {
      const math = mathsExpression(text);
      if (math) {
        return math;
      }
      return unchanged.codespan(text);
    };

    function markedWithKatex(text) {
      return  marked(text, options);
    }

    function absFile(url) {
      this.name=url;
      this.slice = async(offset, length) =>{
        const headers = new Headers();
        headers.append('range', 'bytes=' + offset + '-' + ( offset + length -1).toString());

        const opts = {
          credentials: 'include',
          headers    : headers
        };

        const resp = await fetch( this.name, opts );
        return await resp.arrayBuffer();
        //alert(JSON.stringify(resp.headers));
        //return await resp.text();
      }
    }
    
    var file;
    var blocksize = 20480;
    function loadSlice(){
      function render(u8array){
        document.body.innerHTML = markedWithKatex(new TextDecoder().decode(u8array));
        scroll(0,0);
      }
      function setPageUrl(pageurl){
        let a = document.createElement('a');
        a.href = "i:5c"+pageurl;
        a.click();
      }

      var page = 1;
      var extrasize = blocksize; //extrasize should <= blocksize
      let lhash = location.hash;
      if(lhash){
        if("p"!==lhash.charAt(1)){//#offset=&[len]
          let iAmpersand = lhash.indexOf('&',8);
          let offset = parseInt(lhash.substring(8,iAmpersand));
          let len = parseInt(lhash.substring(iAmpersand+1));
          {//update urlInc() url
            let href = location.href;
            let iHash = href.indexOf('#');
            href=href.substring(0,iHash)+"#page="+Math.floor((offset+len)/blocksize);
            setPageUrl(href);
          }
          file.slice(offset,len).then((buffer)=>{
            let u8 = new Uint8Array(buffer);
            render(u8);
          });
          return;
        }
        //#page=
        page = parseInt(lhash.substring(6));
      }else
        setPageUrl(location.href+"#page=1");
      file.slice(blocksize*(page-1),blocksize+extrasize).then((buffer)=>{
        let u8 = new Uint8Array(buffer);
        let u8len = u8.length;
        let iStart = 0;
        let limit = Math.min(u8len,extrasize);
        if(1!=page){
          for(let i=1;i<limit;i=i+2){
            if(10==u8[i] && 10==u8[i-1]) {
              iStart = i+1;
              break;
            }
          }
        }
        let iEnd = Math.min(u8len,blocksize);
        limit = Math.min(u8len,blocksize+extrasize);
        for(let i=blocksize+1;i<limit;i=i+2){
          if(10==u8[i] && 10==u8[i-1]) {
            iEnd = i+1;
            break;
          }
        }
        render(u8.slice(iStart,iEnd));
      });

    }
    //?url=#offset=&[len]&page=
    {
      let url = location.search.substring(5);
      file = new absFile(url);
      document.getElementsByTagName('base')[0].href=url;
    }
    window.addEventListener('hashchange',loadSlice);
  </script>
</head>
<body>
  <script>loadSlice();</script>
</body>
